package a6;  

/**
    Tests an interpreter for the grammar with start symbol Program
    and productions
<pre>
       Program -> Expr | Local {Local} find Expr
       Local -> with Binding
       Binding -> VariableName Expr | UserFunctionName Definition
       Definition -> begin {VariableName} body Program end 
       Application -> FunctionName ( {Expr} )
       FunctionName -> UserFunctionName | + | -
       Expr -> IntegerLiteral | VariableName | Application
</pre>

    In these productions, the braces and the vertical bar
      are metasymbols, while the parentheses are terminal 
      symbols of the grammar.
    Also,
      <code>UserFunctionName</code> is a preterminal whose 
        corresponding terminal must start with a lower-case letter, 
        <code>VariableName</code> is a preterminal whose 
        corresponding terminal must start with a capital letter, 
        and <code>IntegerLiteral</code> is 
        a legal integer literal in Java
     @author Jeff Smith
     @version for Assignment 6??, CS 152, SJSU, Fall 2012 
 */

public class A6
{
  /**
      Takes an input string purporting to represent a program in 
      L(G), where G is the context-free grammar defined above, and
      prints an appropriately indented representation of the
      result of compiling the string if it is in L(G).
      Otherwise prints an informative error message stating
      why the string is not in the language.
      @param expr the string
   */
  
  private static void test(String program)                                   {
    try                                                                    {  
      System.out.println((new Interpreter().interpret(program)));          }
    catch(IllegalArgumentException e)                                      {
      System.out.println(e.getMessage());                                  } }


  /**
      The main test program
      @param args is ignored
   */ 

  public static void main(String[] args)                                      {
    String newline = System.getProperty("line.separator");
    
    // syntactically and semantically ok
    test("-3");
    test("+ ( 3 4 ) ");
    test("- ( 3 4 )");
    test(" + ( 3 - ( 4 5 ) )");
    test(" + ( - ( 3 4 ) 5 )");
    test("with X -4 find X");
    test("with X + ( 3 4 ) find X");
    test("with X 3 " + newline + "find + ( 1 X )");
    test("with X 3 with Y + ( 1 X ) find Y");
    test("with X 3 with Y + ( X 1 ) find X");
    test("with X 3 with Y + ( X 100 ) find + ( X - ( Y 1 ) )");
    test("with X 3 with f begin Y body + ( X Y  ) end find X");
    test("with X 3 with f begin Y Z body + ( X Z ) end find X");
    test("with X 3 with f begin Y body + ( X Y ) end find f ( 5 )");
    test("with f begin body 50 end find f ( )");
    test("with f begin X body + ( X X ) end " +
         "find f ( 111 )");
    test("with f begin X body + ( X X ) end " +
         "with F f ( 111 ) " +
         "find f ( F )");
    test("with f begin X body + ( X X ) end " +
         "with g begin Y body - ( Y 5 ) end " +
         "find + ( f ( 10 ) g ( 8 ) )");
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find + ( q ( 100 70 ) p ( 4 ) )");
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find p ( q ( 1000 70 ) )");
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "find q ( p ( 100 ) p ( 30 ) )");
    test("with p begin X body + ( X X ) end " +
         "with q begin U V body - ( + ( U V ) 20 ) end " +
         "with r begin Z body q ( p ( Z ) 1 ) end " +
         "find r ( 2000 ) ");
    test("with g begin Y body - ( Y 5 ) end " +
         "with f begin X body " +
         "  + ( g ( X ) X ) end " +
         "find f ( 10 ) ");
    test("with f begin X body " +
         "  with Y 8 " +
         "  find + ( X Y ) end " +
         "find f ( 10 ) ");
    test("with f begin X body " +
         "  with g begin Y body - ( Y 1 ) end " +
         "  find + ( g ( X ) X ) end " +
         "find f ( 10 ) ");
    test("with f begin X body - ( X 10 ) end " +
         "with f begin Y body + ( Y 5 ) end " +
         "find f ( 550 ) ");
    test("with f begin X body + ( X 10 ) end " +
         "with g begin X body " +
         "  with W 2 find f ( + ( W X ) ) end " +
         "find g ( 4 ) ");
    test("with f begin X body + ( g ( X ) 10 ) end " +
         "with g begin X body " +
         "  with f begin W body + ( W 20 ) end " +
         "  find + ( f ( 40 ) X ) end " +
         "find f ( 4 ) ");
    test("with X 999 " +
         "with f begin XX body X end " +
         "find f ( 3 ) ");
    System.out.println();     
         
    // syntactically ok, semantically bad     
    test("X");    
    test("f ( 4 )"); 
    test("- ( 4 )"); 
    test("+ ( 4 5 6 )");  
    test("with X X find X");
    test("with Y + ( D 10 ) find + ( 5 - ( Y 1 ) )");      
    test("with X 2 find + ( X - ( E 1 ) )");      
    test("with Y + ( G 10 ) with G 2 find +  Y 1 )");      
    test("with f1 begin X body + ( Y 10 ) end " +
         "find f1 ( 3 ) ");
    test("with f begin X body + ( X 10 ) end " +
         "find f0 ( 3 ) ");
    test("with f2 begin X body " +
         "  with g2 begin Y body - ( Y 5 ) end " +
         "  find + ( g2 ( X ) X ) end " +
         "find g2 ( 10 ) ");
    test("with f3 begin X body " +
         "  with g3 begin X body h3 ( X 5 ) end " +
         "  find + ( g3 ( X ) X ) end " +
         "find f3 ( 10 ) ");
    test("with f4 begin X body + ( X 10 ) end " +
         "find f4 ( 3 4 ) ");
    test("with f5 begin X1 X2 X3 body + ( X1 X3 ) end " +
         "find f5 ( 3 4 ) ");
    test("with f6 begin X body + ( X 10 ) end " +
         "with f6 begin X Y body + ( Y 10 ) end " +
         "find f6 ( 4 ) ");
    test("with f9 begin X body + ( X X ) end " +
         "find + ( f9 ( 10 ) g9 ( 8 ) )");
    System.out.println();     
    
    // syntactically bad
    test(null);
    test("");
    test("-X");
    test("( 3 )");
    test("3X");    
    test("* ( 3 4 )");
    test("find + ( 3 4 )");
    test(" + ) 3 4 )");
    test(" -  3 4 )");
    test(" + ( 3 4 ");
    test(" + ( 3 + ) ");
    test(" 3 4 )");
    test(" + ( 3 - ( 4 5 ) ) )");
    test("with 3 X find X");
    test("with X1 find X1");
    test("with X2 2");
    test("with X2 3 with Y2 4");
    test("with X3 3 4 find X3");
    test("with X4 3 Y4 4 find ( + X4 Y4 )");
    test("with f1 4 " +
         "find f1 ( 10 )");
    test("with f2 begin X2 body end " +
         "find f2 ( 10 )");
    test("with f3 X3 begin end " +
         "find f3 ( 10 )");
    test("with f4 begin X4 end " +
         "find f4 ( 10 )");
    test("with F6 begin X body + ( X X ) end " +
         "find f6 ( 10 )");
    test("with f8 X body + ( X X ) end " +
         "find f8 ( 10 )");
    test("with begin f9 X body + ( X X ) end " +
         "find f9 ( 10 )");
    test("with f0 begin X + ( X X ) end " +
         "find f0 ( 10 )");
    test("with f2 begin X body ( X X ) end " +
         "find f2 ( 10 )");
    test("with f4 begin X body + ( X X ) end end " +
         "find f4 ( 10 )");
    test("with f5 begin X body + ( X f5 ) end " +
         "find f5 ( 10 )");
    test("with f6 begin X body + ( X X ) end " +
         "find f6 f6 ( 10 )");
    test("with f7 begin X body + ( X X ) end " +
         "find f7 ( f7 )");
    test("with f8 begin X body + ( X X ) end " +
         "find f8 ( 10 ");
    test("with p1 begin X body + ( X 10 ) end " +
         "find end ");    
    test("with p2 begin X body + ( X 10 ) end " +
         "find p2 ");    
    test("with p3 begin X body + ( X 10 ) end " +
         "find ( 3 4 ) ");    
    test("with p4 begin X body + ( X 10 ) end " +
         "find + ( 3 p4 ) ");    
    test("with p5 begin X body + ( X X ) end " +
         "with 3 begin U V body 3 ( U V ) end " +
         "find p5 ( 70 )");
    test("with p6 begin X body + ( X X ) end " +
         "with + begin U V body p6 ( U V ) end " +
         "find p6 ( 70 )");
    test("with p7 begin X body + ( X X ) end " +
         "with q7 begin U V body - ( + ( U V ) 20 ) end " +
         "find p7 ( q7 ( 100 70 ) ");
    test("with p8 begin X body " +
         "  with q8 begin X body - ( X 5 ) " +
         "  find + ( q8 ( X ) X ) end " +
         "find p8 ( 10 ) ");
    test("with p9 begin X body " +
         "  with q9 begin X body - ( X 5 ) end " +
         "  find + ( q9 ( X ) X ) " +
         "find p9 ( 10 ) ");
    test("with p0 begin X body " +
         "  with q0 begin X body - ( X 5 ) end " +
         "  find + ( q0 ( X ) X ) " +
         "find p0 ( 10 ) ");
    test("with f5 begin X body + ( X X ) - ( X 5 ) end " +  // ??
         "find f5 ( 10 )");
    test("with g0 begin f0 X body + ( X X ) end " +
         "find f0 ( 10 )");
    test("with f3 begin X body + ( X X ) " +
         "find f3 ( 10 )");
    System.out.println();     

    // compilation vs. interpretation
    test("with f begin X body + ( X 990 ) end " +
         "with g begin X body end " +
         "find f ( 3 ) ");
    test("with f begin X body + ( X 990 ) end " +
         "with g begin X body end " +
         "find g ( 3 ) ");
    System.out.println();     

    // reused variable names
    // recall the policy of overwriting variable names may give 
    //   some incorrect values, which is ok for this assignment
    // however you should be able to return *some* value
    
    test("with X 1 " +
         "with Y 2 " +
         "with X 3 " +
         "find + ( X Y )");
    test("with X 1 " +
         "with Y 2 " +
         "with f begin W body " +
         "  with X 3 " +
         "  find + ( X W ) end " +
         "find + ( f ( X ) X )");
    test("with X 1 " +
         "with f begin X body + ( X X ) end " +
         "find + ( f ( 10 ) X )");
    test("with f begin X body + ( X X ) end " +
         "with X 1 " +
         "find + ( f ( 10 ) X )");
    test("with f begin X Y X body + ( X + ( Y X ) ) end " +
         "find f ( 1 2 3 ) ");
    test("with f begin X body + ( X 10 ) end " +
         "with g begin X body + ( f ( + ( X 1 ) ) X ) end " +
         "find g ( 2 )");
    test("with X 1 " +
         "with f begin X body + ( X X ) end " +
         "find + ( f ( 10 ) X )");
    test("with f begin X body " +
         "  with g begin Y body " +
         "    with X 110 find - ( Y X ) end " +
         "  find + ( g ( X ) X ) end " +
         "find f ( 100 ) ");
    test("with f begin X body " +
         "  with g begin X body - ( X 5 ) end " +
         "  find + ( g ( + ( X 1 ) ) X ) end " +
         "find  f ( 50 )  ");
    test("with g begin Z body + ( Z 5 ) end " +
         "with f begin X body " +
         "  with g begin Y body - ( Y 5 ) end " +
         "  find + ( g ( + ( X 2 ) ) X ) end " +
         "find + (  f ( 10 ) g ( 10 ) ) ");
    System.out.println();     

    // scoping tests
    test("with f begin X Y body " +
         "  with h begin X W body + ( X - ( Y W ) ) end " +
         "  with g begin Y V body h ( 25 26 ) end " + 
         "  find + ( g ( 15 16 ) h ( 0 1 ) ) end " +
         "find f ( 5 6 ) ");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find g ( 2 )");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find h ( 2 )");
    test("with k begin XX body + ( XX 10 ) end " +
         "with h begin VV body k ( VV ) end " +
         "with g begin W body " + 
         "  with k begin X body + ( X 20 ) end " +
         "  find h ( W ) end " + 
         "find + ( g ( 2 ) h ( 2 ) )");

  
}

}